import { Injectable } from '@nestjs/common';
import { CreateUserDto, ChangePasswordDto, UpdateUserDto } from './dto/user.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { In, Repository } from 'typeorm';
import { CustomException, ErrorCode } from 'src/common/exceptions/custom.exception';
import { Role } from '../role/entities/role.entity';
import { Profile } from './entities/profile.entity';
import { genHash, makeSalt } from 'src/utils/crypt';
import { Request } from 'express';
import { compareValue } from 'src/utils/crypt';
import { PageRequest } from 'src/common/page-request/pageRequest';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRep: Repository<User>,
    @InjectRepository(Role)
    private roleRep: Repository<Role>,
    @InjectRepository(Profile)
    private profileRep: Repository<Profile>,
    private readonly pageRequest: PageRequest,
  ) {}

  /** 新建用户 */
  async create(user: CreateUserDto) {
    const { username } = user;
    const isExist = await this.findByUsername(username);

    if (isExist) {
      throw new CustomException(ErrorCode.ERR_10001);
    }

    const newUser = this.userRep.create(user);
    if (user.roleIds !== undefined) {
      newUser.roles = await this.roleRep.find({
        where: { id: In(user.roleIds) },
      });
    }
    if (!newUser.profile) {
      newUser.profile = this.profileRep.create();
    }

    newUser.password = this.enCryptPassword(newUser.password);

    await this.userRep.save(newUser);
    return true;
  }

  /** 修改密码 */
  async changePassword(req: Request, body: ChangePasswordDto) {
    const user = req.user as User;
    // 先校验密码
    const valid = await this.validateUser(user.username, body.oldPassword);
    if (!valid) {
      throw new CustomException(ErrorCode.ERR_10004);
    }
    const validUser = await this.userRep.findOne({ where: { id: user.id } });
    validUser.password = this.enCryptPassword(body.newPassword);
    await this.userRep.save(validUser);

    return true;
  }

  /** 密码加密 */
  enCryptPassword(password: string) {
    const salt = makeSalt();
    return genHash(password, salt);
  }

  /** 根据用户名查询用户 */
  async findByUsername(username: string) {
    return await this.userRep.findOne({
      where: { username },
      select: ['id', 'username', 'password', 'enable'],
      relations: {
        profile: true,
        roles: true,
      },
    });
  }

  /** 分页查询用户 */
  async getUsers(body: PageRequest) {
    const { pageCondition, filter } = body;
    const skip = (pageCondition.pageIndex - 1) * pageCondition.pageSize;
    const queryCondition = this.pageRequest.genQueryCondition(filter);
    return await this.userRep.find({
      skip, //偏移量
      take: pageCondition.pageSize, //分页
      relations: {
        profile: true,
      },
      where: queryCondition,
    });
  }

  /** 根据用户id更新用户信息 */
  async updateUserById(body: UpdateUserDto) {
    const { id, nickName, email, address, avatar } = body;

    const user = await this.userRep.findOne({
      where: { id },
      relations: { profile: true },
    });
    if (!user) {
      throw new CustomException(ErrorCode.ERR_10005);
    }
    user.profile.nickName = nickName;
    user.profile.email = email;
    user.profile.address = address;
    user.profile.avatar = avatar;
    return this.userRep.save(user);
  }

  /** 根据用户 id 删除用户 */
  async deleteUserById(id: number) {
    const user = await this.userRep.findOne({
      where: { id },
      relations: { profile: true, roles: true },
    });
    if (!user) {
      throw new CustomException(ErrorCode.ERR_10005);
    }

    return await this.userRep.remove(user);
  }

  /** 根据用户id查询用户权限信息 */
  async findByUserId(userId: number) {
    return await this.userRep.findOne({
      where: { id: userId },
      select: ['id', 'username', 'enable', 'roles'],
      relations: {
        // profile: true,
        roles: true,
      },
    });
  }

  /** 校验 */
  async validateUser(username: string, password: string) {
    const user = await this.findByUsername(username);
    if (user && compareValue(password, user.password)) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}
